#include <Arduino.h>
#include <CustomWiFiManager.h>
#include <Wire.h>
#include <DNSServer.h>      
#include <ESP8266HTTPClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266WiFi.h>
#include <coredecls.h>
#include <sys/time.h>
#include <time.h>
#include <Ticker.h>
#include "SSD1306Wire.h"//0.96寸
#include "OLEDDisplayUi.h"
#include "HeFeng.h"
#include "WeatherStationFonts.h"
#include "WeatherStationImages.h"
//温湿度相关
// #define DHT11_PIN D5  // 温度传感器引脚
// DFRobot_DHT11 DHT;
// SHT30 IIC 7位地址为0x44
#define Addr 0x44

int temp = 20;
int hum = 50;
int lasttemp = temp;
int lasthum = hum;
int seconds=0;//时间，降低读取DHT11频率减少卡顿

//GPIO
#define KEY2 D6
#define KEY1 D7
#define LED1 D3//R
#define LED2 D4//G
#define LED3 D8//B
uint8_t R,G,B=0;
uint8_t choseRGB=0;
typedef struct 
{
	uint8_t judge_sta;//处理状态
	bool key_sta;//按键状态
	bool single_flag;//短按
	bool long_flag;//长按
	uint key_time;//计数
} KEYS;
KEYS key[2];
uint8_t lastlong[2]={0,0};//防止出错
//其他
Ticker myTicker;//定时器
ESP8266WebServer server(80);//web

const char* WIFI_SSID = "HUAWEI-E10I2L";  //WIFI名称及密码
const char* WIFI_PWD = "huawei5019";

const char* BILIBILIID = "149235863";  //B站账号

//和风天气接口https://dev.heweather.com/
const char* HEFENG_KEY = "ac34e355a0d94d2ba4f47cf7314823b0";//和风天气秘钥
const char *HEFENG_LOCATION =
    "101020100";  //城市ID,https://github.com/qwd/LocationList/blob/master/China-City-List-latest.csv查询
// const char* HEFENG_LOCATION = "auto_ip";//自动IP定位

#define TZ              8      // 中国时区为8
#define DST_MN          0      // 默认为0

const int UPDATE_INTERVAL_SECS = 5 * 60; // 5分钟更新一次天气
const int UPDATE_CURR_INTERVAL_SECS = 2 * 59; // 2分钟更新一次粉丝数

const int I2C_DISPLAY_ADDRESS = 0x3c;  //I2c地址默认
#if defined(ESP8266)
const int SDA_PIN = 4;  //引脚连接
const int SCL_PIN = 5;  //
#endif

const String WDAY_NAMES[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};  //星期
const String MONTH_NAMES[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};  //月份

//图片转base64工具转换https://tool.css-js.com/base64.html, 64*64

//SH1106Wire     display(I2C_DISPLAY_ADDRESS, SDA_PIN, SCL_PIN);   // 1.3寸
SSD1306Wire     display(I2C_DISPLAY_ADDRESS, SDA_PIN, SCL_PIN);   // 0.96寸
OLEDDisplayUi   ui( &display );

HeFengCurrentData currentWeather; //实例化对象
HeFengForeData foreWeather[3];
HeFeng HeFengClient;

#define TZ_MN           ((TZ)*60)   //时间换算
#define TZ_SEC          ((TZ)*3600)
#define DST_SEC         ((DST_MN)*60)

time_t now; //实例化时间

bool readyForWeatherUpdate = false; // 天气更新标志
bool first = true;  //首次更新标志
long timeSinceLastWUpdate = 0;    //上次更新后的时间
long timeSinceLastCurrUpdate = 0;   //上次天气更新后的时间

String fans = "-1"; //粉丝数

void drawProgress(OLEDDisplay *display, int percentage, String label);   //提前声明函数
void updateData(OLEDDisplay *display);
void updateDatas(OLEDDisplay *display);
void drawDateTime(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawCurrentWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawForecast(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawForecastDetails(OLEDDisplay *display, int x, int y, int dayIndex);
void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state);
void setReadyForWeatherUpdate();
void drawTempAndHum(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);  // 绘制dht11温湿度

//添加框架
//此数组保留指向所有帧的函数指针
//框架是从右向左滑动的单个视图
FrameCallback frames[] = {drawDateTime, drawCurrentWeather, drawForecast, drawTempAndHum};
//页面数量
int numberOfFrames = 4;

OverlayCallback overlays[] = { drawHeaderOverlay }; //覆盖回调函数
int numberOfOverlays = 1;  //覆盖数

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println();

  // 屏幕初始化
  display.init();
  display.clear();
  display.display();

  display.flipScreenVertically(); //屏幕翻转
  display.setContrast(255); //屏幕亮度

  //Web配网
  //webconnect();
  
  // 用固定密码连接
  wificonnect();

  ui.setTargetFPS(30);  //刷新频率

  ui.setActiveSymbol(activeSymbole); //设置活动符号
  ui.setInactiveSymbol(inactiveSymbole); //设置非活动符号

  // 符号位置
  ui.setIndicatorPosition(BOTTOM);

  // 定义第一帧在栏中的位置
  ui.setIndicatorDirection(LEFT_RIGHT);

  // 屏幕切换方向
  // 您可以更改使用的屏幕切换方向 SLIDE_LEFT, SLIDE_RIGHT, SLIDE_TOP, SLIDE_DOWN
  ui.setFrameAnimation(SLIDE_LEFT);

  ui.setFrames(frames, numberOfFrames); // 设置框架
  ui.setTimePerFrame(50000); //设置自动切换时间
  //ui.nextFrame();
  //ui.previousFrame();
  //ui.switchToFrame(uint8_t frame);
  ui.setOverlays(overlays, numberOfOverlays); //设置覆盖

  // UI负责初始化显示
  ui.init();
  display.flipScreenVertically(); //屏幕反转

  configTime(TZ_SEC, DST_SEC, "ntp.ntsc.ac.cn", "ntp1.aliyun.com"); //ntp获取时间

  pinMode(KEY1, INPUT_PULLUP);//GPIO初始化
  pinMode(KEY2, INPUT_PULLUP);
  pinMode(LED1,OUTPUT);
  pinMode(LED2,OUTPUT);
  pinMode(LED3,OUTPUT);

  delay(200);
}

void loop() {

  if (first) {  //首次加载
    updateDatas(&display);
    // 启动Web服务器
    server.on("/", handleRoot);
    server.on("/set", handleSet);
    server.begin();
    myTicker.attach_ms(50,tickerHandle);// 打开定时器
    first = false;  
  }

  server.handleClient();//web处理

  if (millis() - timeSinceLastWUpdate > (1000L * UPDATE_INTERVAL_SECS)) { //屏幕刷新
    setReadyForWeatherUpdate();
    timeSinceLastWUpdate = millis();
  }
  if (millis() - timeSinceLastCurrUpdate > (1000L * UPDATE_CURR_INTERVAL_SECS)) { //粉丝数更新
    HeFengClient.fans(&currentWeather, BILIBILIID);
    fans = String(currentWeather.follower);
    timeSinceLastCurrUpdate = millis();
  }

  if (readyForWeatherUpdate && ui.getUiState()->frameState == FIXED) { //天气更新
    updateData(&display);
  }

  int remainingTimeBudget = ui.update(); //剩余时间预算

  if (remainingTimeBudget > 0) {
    //多余时间，可处理其他
    keyProc();//按键
    DHTProc();//DHT11
    delay(remainingTimeBudget);
  }
  
}

void wificonnect() {  //WIFI密码连接，Web配网请注释
 WiFi.begin(WIFI_SSID, WIFI_PWD);
 while (WiFi.status() != WL_CONNECTED) {
   Serial.print('.');
   delay(80);
   display.clear();
   display.drawXbm(34, 0, bili_Logo_width, bili_Logo_height, bili_Logo_5);
   display.display();
   delay(80);
   display.clear();
   display.drawXbm(34, 0, bili_Logo_width, bili_Logo_height, bili_Logo_6);
   display.display();
   delay(80);
   display.clear();
   display.drawXbm(34, 0, bili_Logo_width, bili_Logo_height, bili_Logo_7);
   display.display();
   delay(80);
   display.clear();
   display.drawXbm(34, 0, bili_Logo_width, bili_Logo_height, bili_Logo_8);
   display.display();
   delay(80);
   display.clear();
   display.drawXbm(34, 0, bili_Logo_width, bili_Logo_height, bili_Logo_1);
   display.display();
   delay(80);
   display.clear();
   display.drawXbm(34, 0, bili_Logo_width, bili_Logo_height, bili_Logo_2);
   display.display();
   delay(80);
   display.clear();
   display.drawXbm(34, 0, bili_Logo_width, bili_Logo_height, bili_Logo_3);
   display.display();
   delay(80);
   display.clear();
   display.drawXbm(34, 0, bili_Logo_width, bili_Logo_height, bili_Logo_4);
   display.display();
 }
 Serial.println("");
 Serial.println(WiFi.localIP());
 Serial.println("");
 delay(500);
}

// void webconnect() {  ////Web配网
//   display.clear();
//   display.drawXbm(0, 0, 128, 64, bilibili); //显示哔哩哔哩
//   display.display();

//   WiFiManager wifiManager;  //实例化WiFiManager
//   wifiManager.setDebugOutput(false); //关闭Debug
//   //wifiManager.setConnectTimeout(10); //设置超时
//   wifiManager.setHeadImgBase64(FPSTR(Icon)); //设置图标
//   wifiManager.setPageTitle("欢迎来到WiFi配置页");  //设置页标题

//   if (!wifiManager.autoConnect("XiaoKai-IOT-Display")) {  //AP模式
//     Serial.println("连接失败并超时");
//     //重新设置并再试一次，或者让它进入深度睡眠状态
//     ESP.restart();
//     delay(1000);
//   }
//   Serial.println("connected...^_^");
//   yield();
// }

void drawProgress(OLEDDisplay *display, int percentage, String label) {    //绘制进度
  display->clear();
  display->setTextAlignment(TEXT_ALIGN_CENTER);
  display->setFont(ArialMT_Plain_10);
  display->drawString(64, 10, label);
  display->drawProgressBar(2, 28, 124, 10, percentage);
  display->display();
}

void updateData(OLEDDisplay *display) {  //天气更新
  HeFengClient.doUpdateCurr(&currentWeather, HEFENG_KEY, HEFENG_LOCATION);
  HeFengClient.doUpdateFore(foreWeather, HEFENG_KEY, HEFENG_LOCATION);
  readyForWeatherUpdate = false;
}

void updateDatas(OLEDDisplay *display) {  //首次天气更新
  drawProgress(display, 0, "Updating fansnumb...");
  HeFengClient.fans(&currentWeather, BILIBILIID);
  fans = String(currentWeather.follower);
  
  drawProgress(display, 33, "Updating weather...");
  HeFengClient.doUpdateCurr(&currentWeather, HEFENG_KEY, HEFENG_LOCATION);
  
  drawProgress(display, 66, "Updating forecasts...");
  HeFengClient.doUpdateFore(foreWeather, HEFENG_KEY, HEFENG_LOCATION);
  
  readyForWeatherUpdate = false;
  drawProgress(display, 100, "Done...");
  delay(200);
  
}

void drawDateTime(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {  //显示时间
  now = time(nullptr);
  struct tm* timeInfo;
  timeInfo = localtime(&now);
  char buff[16];

  display->setTextAlignment(TEXT_ALIGN_CENTER);
  display->setFont(ArialMT_Plain_16);
  String date = WDAY_NAMES[timeInfo->tm_wday];

  sprintf_P(buff, PSTR("%04d-%02d-%02d  %s"), timeInfo->tm_year + 1900, timeInfo->tm_mon + 1, timeInfo->tm_mday, WDAY_NAMES[timeInfo->tm_wday].c_str());
  display->drawString(64 + x, 5 + y, String(buff));
  display->setFont(ArialMT_Plain_24);

  sprintf_P(buff, PSTR("%02d:%02d:%02d"), timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec);
  display->drawString(64 + x, 22 + y, String(buff));
  display->setTextAlignment(TEXT_ALIGN_LEFT);
}

void drawCurrentWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {  //显示天气
  display->setFont(ArialMT_Plain_10);
  display->setTextAlignment(TEXT_ALIGN_CENTER);
  display->drawString(64 + x, 38 + y, currentWeather.cond_txt + "    |   Wind: " + currentWeather.wind_sc + "  ");

  display->setFont(ArialMT_Plain_24);
  display->setTextAlignment(TEXT_ALIGN_LEFT);
  String temp = currentWeather.tmp + "°C" ;
  display->drawString(60 + x, 3 + y, temp);
  display->setFont(ArialMT_Plain_10);
  display->drawString(62 + x, 26 + y, currentWeather.fl + "°C | " + currentWeather.hum + "%");
  display->setFont(Meteocons_Plain_36);
  display->setTextAlignment(TEXT_ALIGN_CENTER);
  display->drawString(32 + x, 0 + y, currentWeather.iconMeteoCon);
}

void drawForecast(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {  //天气预报
  drawForecastDetails(display, x, y, 0);
  drawForecastDetails(display, x + 44, y, 1);
  drawForecastDetails(display, x + 88, y, 2);
}

void drawForecastDetails(OLEDDisplay *display, int x, int y, int dayIndex) {  //天气预报

  display->setTextAlignment(TEXT_ALIGN_CENTER);
  display->setFont(ArialMT_Plain_10);
  display->drawString(x + 20, y, foreWeather[dayIndex].datestr);
  display->setFont(Meteocons_Plain_21);
  display->drawString(x + 20, y + 12, foreWeather[dayIndex].iconMeteoCon);

  String temp = foreWeather[dayIndex].tmp_max + " | " + foreWeather[dayIndex].tmp_min;
  display->setFont(ArialMT_Plain_10);
  display->drawString(x + 20, y + 34, temp);
  display->setTextAlignment(TEXT_ALIGN_LEFT);
}

void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {   //绘图页眉覆盖
  now = time(nullptr);
  struct tm* timeInfo;
  timeInfo = localtime(&now);
  char buff[14];
  sprintf_P(buff, PSTR("%02d:%02d"), timeInfo->tm_hour, timeInfo->tm_min);

  display->setColor(WHITE);
  display->setFont(ArialMT_Plain_10);
  display->setTextAlignment(TEXT_ALIGN_LEFT);
  display->drawString(6, 54, String(buff));
  display->setTextAlignment(TEXT_ALIGN_RIGHT);
  String temp = fans;
  display->drawString(122, 54, temp);
  display->drawHorizontalLine(0, 52, 128);
}

void drawTempAndHum(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,  int16_t y) {// 读取温湿度
  display->setTextAlignment(TEXT_ALIGN_CENTER);
  display->setFont(ArialMT_Plain_24);
  display->drawString(64 + x, 5 + y, String(temp) + "°C | " + String(hum) + "%");
}

void setReadyForWeatherUpdate() {  //为天气更新做好准备
  Serial.println("Setting readyForUpdate to true");
  readyForWeatherUpdate = true;
}

void tickerHandle(){//定时器判断按键
  seconds++;

  key[0].key_sta=digitalRead(KEY1);
  key[1].key_sta=digitalRead(KEY2);

  for(int i=0;i<2;i++){
			switch (key[i].judge_sta){
				case 0:{//识别
					if(key[i].key_sta==0) {
						key[i].judge_sta=1;
						key[i].key_time=0;
					}
				}
				break;
				case 1:{//消抖
					if(key[i].key_sta==0){
						key[i].judge_sta=2;
					}
					else key[i].judge_sta=0;
				}
				break;
				case 2:{//短按
					if(key[i].key_sta==1){
						key[i].judge_sta=0;					
						if(key[i].key_time<20){
							key[i].single_flag=1;
						}
					}
					else {//长按
						key[i].key_time++;
						if(key[i].key_time>20)  
            {
              key[i].long_flag=1;
              key[i].single_flag=0;
            }
					}
				}
				break;	
			}
		}
}

void keyProc(){//按键处理
  if(key[0].single_flag==1){
    Serial.println("K1 short");
    ui.nextFrame();
    key[0].single_flag=0;
  }

  if(key[1].single_flag==1){
    Serial.println("K2 short");
    ui.previousFrame();
    key[1].single_flag=0;
  }

  
  if(key[0].long_flag==1){
    if(lastlong[0]!=key[0].long_flag==1){
      lastlong[0]=key[0].long_flag;
      Serial.println("K1 long");
      if(choseRGB==0){
      if(R>240)R=0;
      else R+=50;
    }
    else if(choseRGB==1){
      if(G>240)G=0;
      else G+=50;
    }
    else if(choseRGB==2){
      if(B>240)B=0;
      else B+=50;
    }
    analogWrite(LED1,R);
    analogWrite(LED2,G);
    analogWrite(LED3,B);
    }
    else lastlong[0]=0;

    key[0].long_flag=0;
    //key[0].key_time=0;
    //key[0].judge_sta=0;
    key[0].single_flag=0;
  }

  if(key[1].long_flag==1){
    if(lastlong[1]!=key[1].long_flag==1){
      lastlong[1]=key[1].long_flag;
      Serial.println("K2 long");
      choseRGB+=1;
      if(choseRGB==3)choseRGB=0;
    }
    else lastlong[1]=0;

    key[1].long_flag=0;
    //key[1].key_time=0;
    //key[1].judge_sta=0;
    key[1].single_flag=0;
  }
}

void DHTProc(){//温湿度

//   if(seconds>200){
//     temp=result.t;
//   if(temp==255)temp=lasttemp;
//     hum=result.rh;
//   if(hum==255)hum=lasthum;
//   seconds=0;
//   }
// }

  unsigned int data[6]; //存储获取到的六个数据
  //开始IIC
  //写地址
  Wire.beginTransmission(Addr);
  //发送测量命令 0x2C06,但是因为IIC一次只能发一个8位数据，所以得发两次
  Wire.write(0x2C);
  Wire.write(0x06);
  //停止IIC
  Wire.endTransmission();
  //等待500ms是等待SHT30器件测量数据，实际上这个时间可以很短
  // delay(500);

  //请求获取6字节的数据，然后会存到8266的内存里
  Wire.requestFrom(Addr, 6);

  //读取6字节的数据
  //这六个字节分别为：温度8位高数据，温度8位低数据，温度8位CRC校验数据
  //               湿度8位高数据，湿度8位低数据，湿度8位CRC校验数据
  if (Wire.available() == 6)
  {
    data[0] = Wire.read();
    data[1] = Wire.read();
    data[2] = Wire.read();
    data[3] = Wire.read();
    data[4] = Wire.read();
    data[5] = Wire.read();
  }

  //然后计算得到的数据，要转化为摄氏度、华氏度、相对湿度
  float cTemp = ((((data[0] * 256.0) + data[1]) * 175) / 65535.0) - 45;
  float fTemp = (cTemp * 1.8) + 32;
  float humidity = ((((data[3] * 256.0) + data[4]) * 100) / 65535.0);

  // //在串口里输出得到的数据
  // Serial.print("相对湿度：");
  // Serial.print(humidity);
  // Serial.println(" %RH");
  // Serial.print("摄氏度温度：");
  // Serial.print(cTemp);
  // Serial.println(" C");
  // Serial.print("华氏度温度：");
  // Serial.print(fTemp);
  // Serial.println(" F");
  if(seconds>200){
    temp=cTemp;
  if(temp==255)temp=lasttemp;
    hum=humidity;
  if(hum==255)hum=lasthum;
  seconds=0;
  }
}


void handleRoot() {//html
  String html = "<html><body>";

  
  html += "<meta charset=\"UTF-8\">";
  html += "<meta http-equiv='Refresh' content='1'>";
  
  html += "<h1>RGB调节</h1>";
  html += "<div><label for='red'>红色：</label>";
  
  html += "<input type='range' id='red' min='0' max='255' value='" + String(R) + "' oninput='changeColor()'/></div>";
  html += "<div><label for='green'>绿色：</label>";
  html += "<input type='range' id='green' min='0' max='255' value='" + String(G) + "' oninput='changeColor()'/></div>";
  html += "<div><label for='blue'>蓝色：</label>";
  html += "<input type='range' id='blue' min='0' max='255' value='" + String(B) + "' oninput='changeColor()'/></div>";
  html += "<div id='color-preview' style='width: 200px; height: 200px; background-color: rgb(" + String(R) + "," + String(G) + "," + String(B) + "); border-radius: 50%;'></div>";
  html += "<br/>";
  


  html += "<p>温度：" + String(temp) + "°C</p>";
  html += "<p>湿度：" + String(hum) + "%</p>";
  
  html += "<script>function changeColor() {";
  html += "var red = document.getElementById('red').value;";
  html += "var green = document.getElementById('green').value;";
  html += "var blue = document.getElementById('blue').value;";
  
  html += "var xhttp = new XMLHttpRequest();";
  html += "xhttp.open('GET', '/set?red=' + red + '&green=' + green + '&blue=' + blue, true);";
  html += "xhttp.send();}</script>";
  html += "</body></html>";
  
  server.send(200, "text/html", html);
}

void handleSet() {
  if (server.hasArg("red") && server.hasArg("green") && server.hasArg("blue")) {
    R = server.arg("red").toInt();
    G = server.arg("green").toInt();
    B = server.arg("blue").toInt();

    analogWrite(LED1, R);
    analogWrite(LED2, G);
    analogWrite(LED3, B);
    
    server.send(200, "text/plain", "OK");
  } else {
    server.send(400, "text/plain", "Bad Request");
  }
}